package com.ikas.ai.utils;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * XML配置文件读写
 * @author Administrator
 */
@SuppressWarnings("all")
public class PropertiesParser {
    private static Logger logger = LoggerFactory.getLogger(PropertiesParser.class);
    private static final String NODEPROPERTY = "Property";
    private static final String NODENAME = "name";
    private static final String NODEVALUE = "value";
    private static final String NODEDESCRIPTION = "description";
    private static final String NODEPROPERTIES = "Properties";
    private static final String ENCODER = "gb2312";
    private static final String XMLINDENT = "\t";
    private String fileName = "config.xml";
    private Document doc;
    private SAXReader reader;
    private Element root;
    private List<Element> propertyList;
    private File xmlFile;
    private String abstractFileName;
    private boolean isDirty = false;
    private String directory;
    private String invokeClassName;

    public PropertiesParser(String filename) {
        this.fileName = filename;
        this.invokeClassName = Thread.currentThread().getStackTrace()[2].getClassName();
    }

    public PropertiesParser(String dir, String name) {
        this.directory = dir;
        this.fileName = name;
    }

    public void toInit() {
        init();
    }

    public void toUnInit() {
        unInit();
    }

    public void init() {
        try {
            initialConfigFile();
            open();
            this.propertyList = this.root.elements(NODEPROPERTY);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void unInit() {
        save();
        this.propertyList = null;
        this.root = null;
    }

    public String getValue(String propertyName, String defaultValue) {
        String name = null;
        String value = null;
        for (Element item : propertyList) {
            try {
                name = item.attributeValue(NODENAME);
                if (name.equals(propertyName)) {
                    value = item.attributeValue(NODEVALUE);
                    break;
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        if (value == null || value.isEmpty()) {
            value = defaultValue;
        }
        return value;
    }

    public void setValue(String propeytyName, String propertyValue, String description) {
        Element el;
        try {
            el = searchElementbyName(propeytyName);
            if (el == null) {
                Element e = this.root.addElement(NODEPROPERTY);
                e.addAttribute(NODENAME, propeytyName);
                e.addAttribute(NODEVALUE, propertyValue);
                e.addAttribute(NODEDESCRIPTION, description);
                this.isDirty = true;
                init();
                logger.debug("Add new property:" + propeytyName);
            } else {
                el.attribute(NODEVALUE).setValue(propertyValue);
                el.attribute(NODEDESCRIPTION).setValue(description);
                this.isDirty = true;
                init();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void setValue(String propeytyName, String propertyValue) {
        Element el;
        try {
            el = searchElementbyName(propeytyName);
            if (el == null) {
                Element e = this.root.addElement(NODEPROPERTY);
                e.addAttribute(NODENAME, propeytyName);
                e.addAttribute(NODEVALUE, propertyValue);
                this.isDirty = true;
                init();
                logger.debug("Add new property:" + propeytyName);
            } else {
                el.attribute(NODEVALUE).setValue(propertyValue);
                this.isDirty = true;
                init();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void initialConfigFile() throws ClassNotFoundException, UnsupportedEncodingException {
        this.reader = new SAXReader();

        if (this.directory != null) {
            this.abstractFileName = this.directory + File.separator + this.fileName;
            return;
        }

        Class<?> invokeClazz = Class.forName(this.invokeClassName);
        String path = URLDecoder.decode(invokeClazz.getProtectionDomain().getCodeSource().getLocation().getFile(), ENCODER);
        path = path.replace("/", File.separator);
        if (path.endsWith(File.separator)) {
            path = path.substring(0, path.length() - 1);
        }

        if (path.startsWith("file:")) {
            path = path.substring(5);
        }

        if (path.endsWith(".jar")) {
            this.abstractFileName = path.substring(0, path.lastIndexOf(File.separator) + File.separator.length()) + this.fileName;
        } else if (path.endsWith(".class")) {
            String splitStr = "classes";
            this.abstractFileName = path.substring(0, path.indexOf(splitStr) + splitStr.length() + File.separator.length()) + this.fileName;
        } else {
            this.abstractFileName = path + File.separator + this.fileName;
        }
    }

    private Element searchElementbyName(String propertyName) {
        String name;
        for (Element item : propertyList) {
            try {
                name = item.attributeValue(NODENAME);
                if (name.equals(propertyName)) {
                    return item;
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        return null;
    }

    public void save() {
        try {
            if (this.isDirty) {
                savexml(this.doc);
                this.isDirty = false;
            }
        } catch (Exception e) {
            logger.error("Save resource to File:" + this.xmlFile.getAbsolutePath());
        }
    }

    private void savexml(Document document) throws IOException {
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setIndent(XMLINDENT);
        format.setEncoding(ENCODER);

        XMLWriter writer = new XMLWriter(new FileWriter(this.abstractFileName), format);

        writer.write(document);
        writer.close();
        logger.debug("Save resource to File:" + this.xmlFile.getAbsolutePath());
    }

    private void createDefaultFile() throws IOException {
        Document doc1 = DocumentHelper.createDocument();
        doc1.setXMLEncoding(ENCODER);

        Element e = doc1.addElement(NODEPROPERTIES).addElement(NODEPROPERTY);
        e.addAttribute(NODENAME, "DateFormatString");
        e.addAttribute(NODEVALUE, "yyyy-MM-dd HH:mm:ss");
        e.addAttribute(NODEDESCRIPTION, "Default DateTime Format String");

        savexml(doc1);
    }


    private void backupFile() {
        SimpleDateFormat sdf;
        try {
            sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            logger.info("Backuping " + this.abstractFileName);
            FileUtil.copyFile(this.xmlFile, new File(this.abstractFileName + ".bak" + sdf.format(new Date())));
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void open() {
        try {
            if (this.doc == null) {
                this.xmlFile = new File(this.abstractFileName);
                if (!(this.xmlFile.exists())) {
                    createDefaultFile();
                }
                try {
                    this.doc = this.reader.read(this.xmlFile);
                } catch (Exception e) {
                    backupFile();
                    logger.error("Parse " + this.abstractFileName + " Error, Creating Default File......");
                    createDefaultFile();
                    this.doc = this.reader.read(this.xmlFile);
                }
                this.root = this.doc.getRootElement();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}
